﻿#region Copyright & License
/*
   Copyright 2009-2010 Stepan Adamec (adamec@yasas.org)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. 
 */
#endregion

namespace See.Sharper {
  using System;
  using System.Threading;

  /// <summary>
  /// 
  /// </summary>
  interface IParallelizer {
    /// <summary>
    /// 
    /// </summary>
    /// <param name="args"></param>
    void Queue(params object[] args);

    /// <summary>
    /// 
    /// </summary>
    void WaitForAll();
  }

  interface IActionParallelizer<TAction> : IParallelizer { }

  interface IFuncParallelizer<TFunction, TStepResult, TResult> : IParallelizer { }

  delegate void FuncCallback<T, TStepResult>(T target, TStepResult stepResult);

  class ActionTParallelizer<T> : IActionParallelizer<Action<T>> {
    private int callbacksCreated = 0, callbacksCompleted = 0;
    private Action<T> actionCaller;

    public ActionTParallelizer(Action<T> action) {
      this.actionCaller = new Action<T>(action);
    }

    #region IActionParallelizer<Action<T>> Members

    public void Queue(params object[] args) {
      AsyncCallback asyncCallback = new AsyncCallback(delegate(IAsyncResult result) {
        try {
          ((Action<T>)result.AsyncState).EndInvoke(result);
        } finally {
          Interlocked.Increment(ref callbacksCompleted);
        }
      });
      callbacksCreated++;

      actionCaller.BeginInvoke((T)args[0], asyncCallback, actionCaller);
    }

    public void WaitForAll() {
      while (callbacksCompleted < callbacksCreated) {
        Thread.Sleep(1);
      }
    }

    #endregion
  }

  class ActionT1T2Parallelizer<T1, T2> : IActionParallelizer<Action<T1, T2>> {
    private int callbacksCreated = 0, callbacksCompleted = 0;
    private Action<T1, T2> actionCaller;

    public ActionT1T2Parallelizer(Action<T1, T2> action) {
      this.actionCaller = new Action<T1, T2>(action);
    }

    #region IParallelizer<Action<T1,T2>> Members

    public void Queue(params object[] args) {
      AsyncCallback asyncCallback = new AsyncCallback(delegate(IAsyncResult result) {
        try {
          ((Action<T1, T2>)result.AsyncState).EndInvoke(result);
        } finally {
          Interlocked.Increment(ref callbacksCompleted);
        }
      });
      callbacksCreated++;

      actionCaller.BeginInvoke((T1)args[0], (T2)args[1], asyncCallback, actionCaller);
    }

    public void WaitForAll() {
      while (callbacksCompleted < callbacksCreated) {
        Thread.Sleep(1);
      }
    }

    #endregion
  }

  class FuncTTResultParallelizer<T, TStepResult, R> : IFuncParallelizer<Func<T, TStepResult>, T, R> {
    private FuncCallback<T, TStepResult> funcCallback;
    private Func<T, TStepResult> funcCaller;
    private int callbacksCreated = 0, callbacksCompleted = 0;

    public FuncTTResultParallelizer(Func<T, TStepResult> func, FuncCallback<T, TStepResult> funcCallback) {
      this.funcCaller = new Func<T, TStepResult>(func);
      this.funcCallback = funcCallback;
    }
    
    #region IFuncParallelizer<Func<T,T,TResult>,R> Members

    public void Queue(params object[] args) {
      AsyncCallback asyncCallback = new AsyncCallback(delegate(IAsyncResult result) {
        try {
          funcCallback(
            (T)args[0], ((Func<T, TStepResult>)result.AsyncState).EndInvoke(result)
          );
        } finally {
          Interlocked.Increment(ref callbacksCompleted);
        }
      });
      Interlocked.Increment(ref callbacksCreated);

      funcCaller.BeginInvoke((T)args[0], asyncCallback, funcCaller);
    }

    public void WaitForAll() {
      while (callbacksCompleted < callbacksCreated) {
        Thread.Sleep(1);
      }
    }

    #endregion
  }
}
